/*
 * Display2D.c
 *
 *  Created on: 22.02.2011
 *      Author: Steffen Hempel und Marco Zeltwanger
 */

#include <stdint.h>
#include "board.h"

#include "boolean.h"
#include "gpio.h"
#include "intc.h"
#include "usart.h"
#include "pdca.h"

#include "pwm.h"
#include "font_8x8.h"
#include "font_5x5.h"

#include "Display2D.h"
#include "viewport2D.h"

static volatile uint8_t rowData[DISPLAY2D_ROW_COUNT][DISPLAY2D_ROW_SIZE];
static volatile uint8_t rowBufferData[DISPLAY2D_ROW_COUNT][DISPLAY2D_ROW_SIZE];

static uint8_t currentRow = 0;

static avr32_pwm_channel_t pwm_channel = { .ccnt = 0 };
static const usart_spi_options_t USART_SPI_DISPLAY_OPTIONS=
{
  .baudrate = 5000000,
  .charlength = 8,
  .spimode = 0,
  .channelmode =USART_NORMAL_CHMODE
};
static const gpio_map_t USART_DISPLAY_GPIO_MAP ={
		{DISPLAY_CLK_PIN, DISPLAY_CLK_FUNCTION},
		{DISPLAY_TX_PIN, DISPLAY_TX_FUNCTION}
};


static inline void  Display2D_toggleRow();

#if __GNUC__
__attribute__((__interrupt__))
#elif __ICCAVR32__
#pragma handler = AVR32_PDCA_IRQ_GROUP, 0
__interrupt
#endif
static void Channel0_int_handler(void)
{
	gpio_set_gpio_pin(DISPLAY_BLANK_PIN);

	Display2D_toggleRow();





	pdca_disable(DISPLAY_PDCA_CHANNEL);
	pdca_disable_interrupt_transfer_complete(DISPLAY_PDCA_CHANNEL);

	gpio_set_gpio_pin(DISPLAY_XLAT_PIN);
	gpio_clr_gpio_pin(DISPLAY_XLAT_PIN);
	gpio_clr_gpio_pin(DISPLAY_BLANK_PIN);
}




void Display2D_refresh()
{


	pdca_disable(DISPLAY_PDCA_CHANNEL);
	pdca_load_channel(DISPLAY_PDCA_CHANNEL, (void *)rowData[currentRow], DISPLAY2D_ROW_SIZE);

	pdca_enable_interrupt_transfer_complete(DISPLAY_PDCA_CHANNEL);
	pdca_enable(DISPLAY_PDCA_CHANNEL);
}


/****************************************************************************************/
/*																						*/
/*	Function : Display2D_toggleRow							Author :  Steffen Hempel 	*/
/*																	  Marco Zeltwanger	*/
/*																						*/
/*																	  					*/
/*	Description : 								   										*/
/*																						*/
/*	Version : 1.0					Date :2011.2.23			Status : Working   			*/
/*	Version : 2.0	OG				Date :2011.2.26			Status : Working   			*/
/*																						*/
/****************************************************************************************/


static inline void Display2D_toggleRow()
{


#define DISPLAY2D_ROW_SELECT_MASK	0x7
#define DISPLAY2D_ROW_SELECT_SHIFT	0x3

	volatile avr32_gpio_port_t *gpio_port =DISPLAY2D_SELECT_PORT;

	uint32_t tmpValue;

	tmpValue=gpio_port->ovr;

	tmpValue&=~(DISPLAY2D_ROW_SELECT_MASK<<DISPLAY2D_ROW_SELECT_SHIFT);
	tmpValue|=~(currentRow<<DISPLAY2D_ROW_SELECT_SHIFT);

	gpio_port->ovr=tmpValue;

	currentRow++;
	currentRow&=DISPLAY2D_ROW_SELECT_MASK;

}

/****************************************************************************************/
/*																						*/
/*	Function : Display2D_repaint										Author :  OG 	*/
/*																	  					*/
/*	Description : 								   										*/
/*																						*/
/*	Version : 1.0					Date :2011.2.27			Status : Working   			*/
/*																						*/
/****************************************************************************************/


void Display2D_repaint()
{
	int counter;

	uint8_t * sourcePointer;
	uint8_t * destinationPointer;

	destinationPointer=(uint8_t *)&rowData[0] [0];
	sourcePointer=(uint8_t *)&rowBufferData [0] [0];

	int a;
	a=DISPLAY2D_ROW_COUNT*DISPLAY2D_ROW_SIZE;

	for (counter=0;counter<(DISPLAY2D_ROW_COUNT*DISPLAY2D_ROW_SIZE) ; counter++)
		{
		*destinationPointer++=*sourcePointer++;
		}

}



/****************************************************************************************/
/*																						*/
/*	Function : Display2D_setPixel							Author :  Steffen Hempel 	*/
/*																	  Marco Zeltwanger	*/
/*																						*/
/*																	  					*/
/*	Description : 								   										*/
/*																						*/
/*	Version : 1.0					Date :2011.2.23			Status : Working   			*/
/*	Version : 2.0	OG				Date :2011.2.26			Status : Working   			*/
/*																						*/
/****************************************************************************************/

boolean Display2D_setPixel(uint8_t x, uint8_t y, uint16_t color)
{
	uint8_t red;
	uint8_t green;
	uint8_t blue;

	uint8_t tmpX;

	if ((x > (DISPLAY2D_COLUMN_COUNT-1)) ||(y > (DISPLAY2D_ROW_COUNT-1)) ||
		(x<0) || (y<0)	)
		{
		return(FALSE);
		}



	// red green blue
	red= (color &0xf)<<4;
	green=( color &0xf0);
	blue= (color &0xf00)>>4;


	if ((x & 0x1) ==0)
		{
		tmpX=((3*x)>>1);
		rowBufferData[y] [DISPLAY2D_RED_START+tmpX]= red;
		rowBufferData[y] [DISPLAY2D_RED_START+tmpX+1]&=0x0f;

		rowBufferData[y] [DISPLAY2D_BLUE_START+tmpX]= blue;
		rowBufferData[y] [DISPLAY2D_BLUE_START+tmpX+1]&=0x0f;

		tmpX=((((DISPLAY2D_ROW_COUNT-1)-x))*3>>1);

		rowBufferData[y] [DISPLAY2D_GREEN_START+tmpX]&=0xf0;
		rowBufferData[y] [DISPLAY2D_GREEN_START+tmpX]|= green>>4;
		rowBufferData[y] [DISPLAY2D_GREEN_START+tmpX+1]|= green<<4;
		}
	else
		{
		tmpX=((3*x)>>1);

		rowBufferData[y] [DISPLAY2D_RED_START+tmpX]&=0xf0;
		rowBufferData[y] [DISPLAY2D_RED_START+tmpX]|= red>>4;
		rowBufferData[y] [DISPLAY2D_RED_START+tmpX+1]|= red<<4;

		rowBufferData[y] [DISPLAY2D_BLUE_START+tmpX]&=0xf0;
		rowBufferData[y] [DISPLAY2D_BLUE_START+tmpX]|= blue>>4;
		rowBufferData[y] [DISPLAY2D_BLUE_START+tmpX+1]|= blue<<4;

		tmpX=((((DISPLAY2D_ROW_COUNT-1)-x))*3>>1);
		rowBufferData[y] [DISPLAY2D_GREEN_START+tmpX]= green;
		rowBufferData[y] [DISPLAY2D_GREEN_START+tmpX+1]&=0x0f;

		}
return(TRUE);
}

/****************************************************************************************/
/*																						*/
/*	Function : Display2D_setLED								Author :  Steffen Hempel 	*/
/*																	  Marco Zeltwanger	*/
/*																						*/
/*																	  					*/
/*	Description : 								   										*/
/*																						*/
/*	Version : 1.0					Date :2011.2.23			Status : Working   			*/
/*	Version : 2.0	OG				Date :2011.2.26			Status : Working   			*/
/*																						*/
/****************************************************************************************/

boolean Display2D_setLED(uint8_t index, uint16_t value)
{
	int row;
	int column;

	row = index/4;
	column = 3- (index- (row *4));

	uint8_t red;
	uint8_t green;



	if ((index<0) || (row >3))
	{
		return(FALSE);
	}


	red= value &0xf;
	green=( value &0xf0)>>4;

	if (row==3)
		{
		row=7;
		}

	column=(3*column);
	rowBufferData[row] [column]= green;
	rowBufferData[row] [column+1]=0x00;
	rowBufferData[row] [column+2]=0x00;

	rowBufferData[row] [column+1]&=0xf0;
	rowBufferData[row] [column+1]|= red>>4;
	rowBufferData[row] [column+2]|= red<<4;

	return(TRUE);
}

/****************************************************************************************/
/*																						*/
/*	Function : Display2D_clear											Author :  OG 	*/
/*																	  					*/
/*	Description : 								   										*/
/*																						*/
/*	Version : 1.0					Date :2011.2.27			Status : Working   			*/
/*																						*/
/****************************************************************************************/

void Display2D_clear()
{
	int y;

	for (y = 0; y < DISPLAY2D_ROW_COUNT; ++y)
	{
		int x;

		for (x = 0; x < DISPLAY2D_ROW_SIZE; ++x)
		{
			rowBufferData[y][x] = 0x0;
		}
	}
}


/****************************************************************************************/
/*																						*/
/*	Function : Display2D_clear											Author :  OG 	*/
/*																	  					*/
/*	Description : 								   										*/
/*																						*/
/*	Version : 1.0					Date :2011.2.27			Status : Working   			*/
/*																						*/
/****************************************************************************************/

void Display2D_fillAll(uint16_t color)
{
	int counter;


	uint8_t * destinationPointer;

	destinationPointer=(uint8_t *)&rowBufferData [0] [0];

	for (counter=0;counter<(DISPLAY2D_ROW_COUNT*DISPLAY2D_ROW_SIZE) ; counter++)
		{
		*destinationPointer++=color;
		}



}

/****************************************************************************************/
/*																						*/
/*	Function : Display2D_init								Author :  Steffen Hempel 	*/
/*																	  Marco Zeltwanger	*/
/*																						*/
/*																	  					*/
/*	Description : 								   										*/
/*																						*/
/*	Version : 1.0					Date :2011.2.23			Status : Working   			*/
/*	Version : 2.0	OG				Date :2011.2.26			Status : Working   			*/
/*																						*/
/****************************************************************************************/

void Display2D_init()
{
	// Grayscale clock pwm_channel init
	gpio_enable_module_pin(GS_PWM_PIN, GS_PWM_FUNCTION);
	pwm_channel.CMR.calg = PWM_MODE_LEFT_ALIGNED;       // Channel mode.
	pwm_channel.CMR.cpol = PWM_POLARITY_HIGH;            // Channel polarity.
	pwm_channel.CMR.cpd = PWM_UPDATE_DUTY;              // Not used the first time.
	pwm_channel.CMR.cpre = AVR32_PWM_CPRE_MCK;  // Channel prescaler.
	pwm_channel.cdty = 3;   // Channel duty cycle, should be < CPRD.
	pwm_channel.cprd = 6;  // Channel period.
	pwm_channel.cupd = GS_PWM_CHANNEL_ID;   // Channel update is not used here.
	pwm_channel_init(GS_PWM_CHANNEL_ID, &pwm_channel); // Set channel configuration to channel 0.
	pwm_start_channels(1<<GS_PWM_CHANNEL_ID);

	Display2D_clear();

	gpio_local_enable_pin_output_driver(DISPLAY_BLANK_PIN);
	gpio_local_enable_pin_output_driver(DISPLAY_XLAT_PIN);
	gpio_local_enable_pin_output_driver(DISPLAY_ROW_SELECT_PIN_0);
	gpio_local_enable_pin_output_driver(DISPLAY_ROW_SELECT_PIN_1);
	gpio_local_enable_pin_output_driver(DISPLAY_ROW_SELECT_PIN_2);

	gpio_clr_gpio_pin(DISPLAY_ROW_SELECT_PIN_0);
	gpio_clr_gpio_pin(DISPLAY_ROW_SELECT_PIN_1);
	gpio_clr_gpio_pin(DISPLAY_ROW_SELECT_PIN_2);

	gpio_enable_module(USART_DISPLAY_GPIO_MAP,
			sizeof(USART_DISPLAY_GPIO_MAP) / sizeof(USART_DISPLAY_GPIO_MAP[0]));

	usart_init_spi_master((DISPLAY_USART), &USART_SPI_DISPLAY_OPTIONS,PERIPHERIAL_CLOCK);

	static const pdca_channel_options_t PDCA_OPTIONS =
	{
			.addr = (void *)rowData[0],   	// memory address
			.pid = DISPLAY_TX_PDCA,       		 // select peripheral - data are transmit on SPI line.
			.size = DISPLAY2D_ROW_SIZE,      // transfer counter
			.r_addr = NULL, 					// next memory address
			.r_size = 0,	  					 // next transfer counter
			.transfer_size = PDCA_TRANSFER_SIZE_BYTE,                // select size of the transfer
	};


	pdca_init_channel(DISPLAY_PDCA_CHANNEL, &PDCA_OPTIONS); // init PDCA channel with options.

	Disable_global_interrupt();
	INTC_register_interrupt( (__int_handler) &Channel0_int_handler, DISPLAY_PDCA_IRQ, AVR32_INTC_INT1);


	pdca_enable_interrupt_transfer_complete(DISPLAY_PDCA_CHANNEL);

	Enable_global_interrupt();

	gpio_clr_gpio_pin(DISPLAY_BLANK_PIN);

}



/****************************************************************************************/
/*																						*/
/*	Function : Display2D_print8x8Char									Author :  OG 	*/
/*																	  					*/
/*	Description : 								   										*/
/*																						*/
/*	Version : 1.0					Date :2011.3.25			Status : Working   			*/
/*																						*/
/****************************************************************************************/

boolean Display2D_print8x8Char(uint8_t index,uint16_t color,int32_t xPos, int32_t yPos)
{
	uint8_t characterLine;
	int8_t lineCounter;
	int8_t columnCounter;

	if ((index<32) || (index>128) )
		return(FALSE);

	index-=32;



	if ((xPos<-7) || (xPos>7) ||
			(yPos<-7) || (yPos>7))
		 return(FALSE);


	for (lineCounter=0;lineCounter<8;lineCounter++)
		{
		characterLine=Font_8x8[index] [7-lineCounter];
		for (columnCounter=7;columnCounter>=0;columnCounter--)
			{
			if ((characterLine & 0x1)>0)
				{
				Display2D_setPixel(xPos+ columnCounter, yPos+ lineCounter, color);
				}
				characterLine>>=1;
			}

		}


	//Display2D_setPixel(uint8_t x, uint8_t y, uint32_t value)

	return(true);
}


/****************************************************************************************/
/*																						*/
/*	Function : Display2D_print5x5Char									Author :  OG 	*/
/*																	  					*/
/*	Description : 								   										*/
/*																						*/
/*	Version : 1.0					Date :2011.3.26			Status : Working   			*/
/*																						*/
/****************************************************************************************/

boolean Display2D_print5x5Char(uint8_t index,uint16_t color,int32_t xPos, int32_t yPos)
{
	uint8_t characterLine;
	int8_t lineCounter;
	int8_t columnCounter;

	if ((index<32) || (index>128) )
		return(FALSE);

	index-=32;



	if ((xPos<-4) || (xPos>7) ||
			(yPos<-4) || (yPos>7))
		 return(FALSE);


	for (lineCounter=0;lineCounter<5;lineCounter++)
		{
		characterLine=Font_5x5[index] [4-lineCounter];
		for (columnCounter=4;columnCounter>=0;columnCounter--)
			{
			if ((characterLine & 0x1)>0)
				{
				Display2D_setPixel(xPos+ columnCounter, yPos+ lineCounter, color);
				}
				characterLine>>=1;
			}

		}


	//Display2D_setPixel(uint8_t x, uint8_t y, uint32_t value)

	return(true);
}

/****************************************************************************************/
/*																						*/
/*	Function : Display2D_dravLine										Author :  OG 	*/
/*																	  					*/
/*	Description : 								   										*/
/*																						*/
/*	Version : 1.0					Date :2011.3.28			Status : Working   			*/
/*																						*/
/****************************************************************************************/

void Display2D_dravLine(int8_t x,int8_t y,int8_t dx,int8_t dy,uint16_t color )
{
	int counter;
	int8_t tmpPoint;

	int8_t xa;
	int8_t ya;


	if (dx<0)
		{
		xa=-dx;
		}
	else
		{
		xa=dx;
		}

	if (dy<0)
		{
		ya=-dy;
		}
	else
		{
		ya=dy;
		}


	Display2D_setPixel(x,y,color);

	if (xa>ya)
	{
		for (counter=1;counter<=xa;counter++)
			{
			tmpPoint=y+(dy*(counter)/xa);
			if (dx>=0)
				{
				Display2D_setPixel(x+counter,tmpPoint,color);
				}
			else
				{
				Display2D_setPixel(x-counter,tmpPoint,color);
				}
			}
	}

	else
		{
		for (counter=1;counter<=ya;counter++)
			{
			tmpPoint=x+(dx*(counter)/ya);
			if (dy>=0)
				{
				Display2D_setPixel(tmpPoint,y+counter,color);
				}
			else
				{
				Display2D_setPixel(tmpPoint,y-counter,color);
				}
			}

		}


}



/****************************************************************************************/
/*																						*/
/*	Function : Display2D_displayIcon									Author :  OG 	*/
/*																	  					*/
/*	Description : 								   										*/
/*																						*/
/*	Version : 1.0					Date :2011.3.28			Status : Working   			*/
/*																						*/
/****************************************************************************************/

void Display2D_displayIcon(uint16_t * iconData)
{
	uint8_t xCounter;
	uint8_t yCounter;

	Display2D_clear();

	for (yCounter=0;yCounter<8;yCounter++)
		for (xCounter=0;xCounter<8;xCounter++)
		{
			Display2D_setPixel(xCounter,7-yCounter,*iconData++);
		}


	Display2D_repaint();

}



/****************************************************************************************/
/*																						*/
/*	Function : Display2D_display2DBitmap					Author : OG					*/
/*																						*/
/*	Description : 								   										*/
/*																						*/
/*	Version : 1.0					Date :2011.4.30			Status : working   			*/
/*																						*/
/****************************************************************************************/

void Display2D_display2DBitmap(Dimension2D_t * origin, Dimension2D_t * size,uint16_t * addres,uint16_t * viewport)
{


	uint16_t xCounter;
	uint16_t yCounter;

	int16_t xExtend;
	int16_t yExtend;

	uint16_t vpXOrigin;
	uint16_t vpXSize;

	uint16_t vpYOrigin;
	uint16_t vpYSize;

	uint16_t color;

	vpXOrigin=Viewport2D_getXOrigin(viewport);
	vpYOrigin=Viewport2D_getYOrigin(viewport);

	vpXSize=Viewport2D_getXSize(viewport);
	vpYSize=Viewport2D_getYSize(viewport);



	if (vpXSize> ((size->x)-vpXOrigin ))
		{
//		xExtend=size->x-origin->x ;
		xExtend=size->x-vpXOrigin;
		}
	else
		{
		xExtend=vpXSize;
		}


	if (xExtend>DISPLAY2D_COLUMN_COUNT-origin->x)
		xExtend=DISPLAY2D_COLUMN_COUNT-origin->x;





	if (vpYSize> (size->y - vpYOrigin ))
		{
		//yExtend=size->y-origin->y ;
		xExtend=size->x-vpXOrigin;
		}
	else
		{
		yExtend=vpYSize;
		}


	if (yExtend>DISPLAY2D_ROW_COUNT-origin->y)
		yExtend=DISPLAY2D_ROW_COUNT-origin->y;



	for (yCounter=0;yCounter<yExtend;yCounter++)
			for (xCounter=0;xCounter<xExtend;xCounter++)
				{
				color=*(addres+(((vpYOrigin+yCounter)* size->x )+(vpXOrigin+xCounter)));
				Display2D_setPixel(origin->x+xCounter, origin->y+yCounter,color);
				}


/*

	uint16_t xCounter;
	uint16_t yCounter;
	uint16_t xExtend;
	uint16_t yExtend;

	uint16_t *position;
	xCounter=0;
	yCounter=0;




	if ((size.x-origin.x)>=DISPLAY2D_COLUMN_COUNT)
		xExtend=DISPLAY2D_COLUMN_COUNT;
	else
		{
		xExtend=size.x-origin.x;
		}

	if ((size.y-origin.y)>=DISPLAY2D_ROW_COUNT)
		yExtend=DISPLAY2D_ROW_COUNT;
	else
		{
		yExtend=size.y-origin.y;
		}



		for (yCounter=0;yCounter<yExtend;yCounter++)
			{
			position=addres+origin.x+(yCounter*(size.x));

			for (xCounter=0;xCounter<xExtend;xCounter++)
				{
				Display2D_setPixel(origin.x+xCounter,origin.y+yCounter, *position++);
				}
			}

*/

	// from x= origin.x  y= origin.y ;


/* JAVA CODE
	int xCounter;
	int yCounter;

	int xExtend;
	int yExtend;

	if (viewport.xSize> (bitmap.getXSize()-viewport.xOrigin ))
		{
		xExtend=bitmap.getXSize()-xOrigin ;
		}
	else
		{
		xExtend=viewport.xSize;
		}


	if (xExtend>Display2D.getColumnCount()-xOrigin)
		xExtend=Display2D.getColumnCount()-xOrigin;



	if (viewport.ySize> (bitmap.getYSize()-viewport.yOrigin ))
		{
		yExtend=bitmap.getYSize()-yOrigin ;
		}
	else
		{
		yExtend=viewport.ySize;
		}


	if (yExtend>Display2D.getRowCount()-yOrigin)
		yExtend=Display2D.getRowCount()-yOrigin;

	// TODO murks !!!!


	for (yCounter=0;yCounter<yExtend;yCounter++)
		for (xCounter=0;xCounter<xExtend;xCounter++)
			{
			Display2D.setPixel(xOrigin+xCounter,yOrigin+yCounter,bitmap.getPixel(viewport.xOrigin +xCounter, viewport.yOrigin+yCounter));
			}

}


*/
}
